#include "testable_engine.h"
#include <sstream>
#include <iostream>
#include "file.h"
#include "error.h"
#include "mock_file.h"
#include "stdarg.h"

namespace VERGE1 {

    TestableEngine testable_engine;

    File* TestableEngine::find_mock_file(const char* filename) {
        mock_file_list_t::iterator match = this->mock_file_list.find(filename);
        if (match == this->mock_file_list.end()) {
            return 0;
        }
        return match->second;
    }
    
    bool TestableEngine::is_read_mode(const char* mode) {
        return !strcmp(mode, "r");
    }
    
    bool TestableEngine::is_write_mode(const char* mode) {
        return !strcmp(mode, "w");
    }
    
    void TestableEngine::validate_file(File* file) {
        if (!file) {
            err("file is null");
        }
    }

    File* TestableEngine::fopen(const char* filename, const char* mode) {
        std::cout << "TestableEngine::fopen " << filename << std::endl;
        if (!find_mock_file(filename)) {
            if (is_read_mode(mode)) {
                return 0;
            }
            this->mock_file_list[filename] = new MockFile();
        }
        File* mock_file = this->mock_file_list[filename];
        if (!mock_file) {
            err("mock file is null");
        }
        if (is_write_mode(mode)) {
            
        }
        return mock_file;
    }
    
    int TestableEngine::fclose(File*& file) {
        std::cout << "TestableEngine::fclose" << std::endl;
        validate_file(file);
        ((MockFile*)file)->reset();
        return 1;
    }
    
    int TestableEngine::fscanf(File* file, const char* format, ...) {
        std::cout << "TestableEngine::fscanf" << std::endl;
        validate_file(file);
        va_list list;
        va_start(list, format);
        
        const char* p = format;
        std::stringstream* stream = ((MockFile*)file)->mock_stream;
        const int ITERATION_LIMIT = 1024;
        int iteration = 0;
        while (*p) {
            if (*p == '%') {
                ++p;
                if (*p == 's') {
                    char* arg = va_arg(list, char*);
                    std::string s;
                    *stream >> s;
                    strcpy(arg, s.c_str());
                } else if (*p == 'd') {
                    int* arg = va_arg(list, int*);
                    *stream >> *arg;
                }
            }
            ++p;
            ++iteration;
            if (iteration >= ITERATION_LIMIT) {
                err("too many iterations in fscanf; potential infinite loop");
            }
        }
        va_end(list);
        return 0;
    }
    
    char* TestableEngine::fgets(char* s, int n, File* file) {
        std::cout << "TestableEngine::fgets" << std::endl;
        validate_file(file);
        std::stringstream* stream = ((MockFile*)file)->mock_stream;
        stream->getline(s, n);
        return s;
    }
    
    int TestableEngine::fwrite(const void* p, int size, int count, File* file) {
        std::cout << "UnitTestableEngine::fwrite" << std::endl;
        std::stringstream* stream = ((MockFile*)file)->mock_stream;
        *stream << (const char*)p;
        return count;
    }
    
    int TestableEngine::remove(const char* filename) {
        std::cout << "TestableEngine::remove" << std::endl;
        if (!find_mock_file(filename)) {
            return 0;
        }
        MockFile* file = (MockFile*)this->mock_file_list[filename];
        this->mock_file_list.erase(filename);
        delete file;
        return 1;
    }
}
